﻿using Microsoft.AspNetCore.Http;
using Serilog;
using System.Security;
using System.Text;

namespace Luka;

/// <summary>
/// 加密中间件
/// </summary>
[NotScanned]
public sealed class EncryptionMiddleware
{
    /// <summary>
    /// 请求委托
    /// </summary>
    private readonly RequestDelegate _next;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="next"></param>
    public EncryptionMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    /// <summary>
    /// 处理HTTP请求管道中的请求
    /// </summary>
    public async Task Invoke(HttpContext context)
    {
        try
        {
            var timestamp = context.Request.Headers["timestamp"].ToString();
            var signature = context.Request.Headers["signature"].ToString();
            //时间戳验证.校验请求时间是否超出期限
            if (string.IsNullOrWhiteSpace(timestamp))
            {
                DateTime dateTime = DateTimeOffset.FromUnixTimeSeconds(Convert.ToInt64(timestamp)).UtcDateTime;
                if ((DateTime.UtcNow - dateTime).TotalSeconds > 90)
                {
                    Log.Error($"数据加密：接口请求超过90秒！【Url】{context.Request.Path},【IP】{context.Connection.RemoteIpAddress?.ToString()},【signature】{signature}");
                    throw new SecurityException("请求错误！");
                }
            }
            //验证签名
            if (string.IsNullOrWhiteSpace(signature))
            {
                Log.Error($"数据加密：签名错误！【Url】{context.Request.Path},【IP】{context.Connection.RemoteIpAddress?.ToString()},【signature】{signature}");
                throw new SecurityException("请求错误！");
            }
            //Body 解码后重新写入
            if (context.Request.Body != null)
            {
                //解密请求体
                string decryptedBody;
                using (var reader = new StreamReader(context.Request.Body))
                {
                    string encryptedBody = await reader.ReadToEndAsync();
                    decryptedBody = Decrypt(encryptedBody, context);
                }
                //重写请求流
                byte[] requestData = Encoding.UTF8.GetBytes(decryptedBody);
                context.Request.Body = new MemoryStream(requestData);

                //调度到原始路由
                await _next(context);

                //加密响应体
                string responseBody;
                using (var reader = new StreamReader(context.Response.Body))
                {
                    context.Response.Body.Seek(0, SeekOrigin.Begin);
                    responseBody = await reader.ReadToEndAsync();
                }
                string encryptedResponse = Encrypt(responseBody, context);
                byte[] responseData = Encoding.UTF8.GetBytes(encryptedResponse);
                context.Response.Body = new MemoryStream(responseData);
            }
            else
            {
                await _next(context);
            }
        }
        catch (Exception)
        {
            throw;
        }
    }

    /// <summary>
    /// 实现解密逻辑
    /// </summary>
    /// <returns></returns>
    private static string Decrypt(string encryptedBody, HttpContext context)
    {
        try
        {
            var encryptionHandler = InternalApp.GetService<IEncryptionHandler>();
            encryptionHandler?.Decrypt(encryptedBody, context);
            return encryptedBody;
        }
        catch (Exception)
        {
            Log.Error($"数据加密：解密失败！【Url】{context.Request.Path},【IP】{context.Connection.RemoteIpAddress?.ToString()},【EncryptedData】{encryptedBody}");
            throw;
        }
    }

    /// <summary>
    /// 实现加密逻辑
    /// </summary>
    /// <returns></returns>
    private static string Encrypt(string responseBody, HttpContext context)
    {
        try
        {
            var encryptionHandler = InternalApp.GetService<IEncryptionHandler>();
            encryptionHandler?.Decrypt(responseBody, context);
            return responseBody;
        }
        catch (Exception)
        {
            Log.Error($"数据加密：加密失败！【Url】{context.Request.Path},【IP】{context.Connection.RemoteIpAddress?.ToString()},【OriginalData】{responseBody}");
            throw;
        }
    }
}